#include "Sources.h"

int sources_syntax_on__ = 1;


Sources::Sources()
{
    mCur = NULL;
    mList_head = NULL;
}

/* --------- */
/* Functions */
/* --------- */
/* Add:  Add a file to the list of source files.
 * -----------
 *
 *   path:   Full path to the source file (this is considered to be a
 *           unique identifier -- no duplicate paths in the list!)
 *
 * Return Value:  Zero on success, non-zero on error.
 */
int Sources::Add(const char *path)
{
    list_node *new_node; 

    new_node = (list_node*) malloc(sizeof(list_node));
    new_node->path         = strdup(path);
    new_node->lpath        = NULL;
    new_node->buf.length   = 0;
    new_node->buf.tlines    = NULL;    /* This signals an empty buffer */
    new_node->sel_line     = 0;
    new_node->sel_col      = 0;
    new_node->sel_col_rbeg = 0;
    new_node->sel_col_rend = 0;
    new_node->sel_rline    = 0;
    new_node->exe_line     = 0;
    new_node->last_modification = 0; /* No timestamp yet */

    if (mList_head == NULL){
        /* List is empty, this is the first node */
        new_node->next = NULL;
        mList_head = new_node;
    }
    else{
        /* Insert at the front of the list (easy) */
        new_node->next = mList_head;
        mList_head = new_node;
    }

    return 0;
}

/* source_set_relative_path: Sets the path that gdb uses for breakpoints
 * -------------------------
 * 
 *   path:   Full path to the source file (this is considered to be a
 *           unique identifier -- no duplicate paths in the list!)
 *   lpath:  
 *
 * Return Value:  Zero on success, non-zero on error.
 */
int Sources::Set_relative_path(const char *path, const char *lpath)
{
    list_node *node = mList_head;

    while ( node != NULL ) {
        if ( strcmp(node->path, path ) == 0) {
            node->lpath = strdup(lpath);
            return 0;
        }

        node = node->next;
    } 

    return -1;
}

/* source_del:  Remove a file from the list of source files.
 * -----------
 *
 *   path:   Full path to the source file to remove from the list.  If the
 *           file is buffered in memory, it'll be freed.
 *
 * Return Value:  Zero on success, non-zero on error.
 */ 
int Sources::Del(const char *path)
{
    list_node *cur;
    list_node *prev = NULL;
    int i; 

    /* Find the target node */
    for (cur = mList_head; cur != NULL; cur = cur->next){
        if (strcmp(path, cur->path) == 0)
            break;
        prev = cur;
    }

    if (cur == NULL)
        return 1;      /* Node not found */

    /* Release file buffer, if one is in memory */   
    if (cur->buf.tlines){
        for (i = 0; i < cur->buf.length; i++) {
            free(cur->buf.tlines[i]);
            if ( cur->buf.cur_line ) {
                free(cur->buf.cur_line);
                cur->buf.cur_line = NULL;
            }
            free(cur->orig_buf.tlines[i]);
        }
    }
    free(cur->buf.tlines);
    free(cur->orig_buf.tlines);

    /* Release the breakpoints */
    if (cur->buf.breakpts) {
        free (cur->buf.breakpts);
        cur->buf.breakpts = NULL;
    }

    /* Release file name */
    free(cur->path);
    cur->path = NULL;

    /* Release local file name */
    if (cur->lpath) {
        free(cur->lpath);
        cur->lpath = NULL;
    }

    /* Remove link from list */
    if (cur == mList_head)
        mList_head = mList_head->next;
    else
        prev->next = cur->next;

    /* Free the node */
    free(cur);

    return 0;
}

/* source_length:  Get the length of a source file.  If the source file hasn't
 * --------------  been buffered already, it will be loaded into memory.
 *
 *   path:   Full path to the source file
 *
 * Return Value:  Length of file on success, -1 on error.
 */
int Sources::Length(const char *path)
{
    list_node *cur = Get_node(path);

    if (!cur){
        /* Load the file if it's not already */
        if (!cur->buf.tlines && Load_file(cur))
            return -1;
    }

    return cur->buf.length;
}

/* source_current_file: Get the name of the current file being displayed.
 * --------------------
 *
 *  path: The path to the current file being displayed.
 *  
 *  Return Value: NULL if no file is being displayed, otherwise a pointer to
 *                the current path of the file being displayed.
 */
char* Sources::Current_file(char *path)
{
    if (mCur == NULL)
        return NULL;

    strcpy(path, mCur->path);
    return path;
}

/* source_display:  Display a portion of a file in a curses window.
 * ---------------
 *
 *   focus:  If the window should have focus
 *
 * Return Value:  Zero on success, non-zero on error.
 */
int Sources::Display(int focus)
{
    char fmt[5];
    int width, height;
    int lwidth;
    int line;
    int i;
    int attr = 0, sellineno;

    if (hl_groups_get_attr(hl_groups_instance, HLG_SELECTED_LINE_NUMBER, &sellineno) == -1)
        return -1;

    /* Check that a file is loaded */
    if (mCur == NULL || mCur->buf.tlines == NULL){
        logo_display(&m_window);
        Window::Display(focus);
        m_window.noutrefresh();
        return 0;
    }

    /* Make sure cursor is visible */
    if (focus)
        curs_set(1);
    else
        curs_set(0);

    /* Initialize variables */
    m_window.getmaxyx(height, width);

    /* Set starting line number (center source file if it's small enough) */
    if (mCur->buf.length < height)
        line = (mCur->buf.length - height) / 2;
    else{
        line = mCur->sel_line - height / 2;
        if (line > mCur->buf.length - height)
            line = mCur->buf.length - height;
        else if (line < 0)
            line = 0;
    }

    /* Print 'height' lines of the file, starting at 'line' */
    lwidth = (int) log10(mCur->buf.length)+1;
    sprintf(fmt, "%%%dd", lwidth);

    for (i = 0; i < height; i++, line++){
        m_window.move(i, 0);
        if (has_colors()){
            /* Outside of file, just finish drawing the vertical line */
            if (line < 0 || line >= mCur->buf.length){
                int j;
                for (j = 1; j < lwidth; j++){
                    m_window.addch(' ');
                }
                m_window.addch('~');
                if (focus){
                    m_window.attron(A_BOLD);
                }
                m_window.addch(VERT_LINE);
                if (focus){
                    m_window.attroff(A_BOLD);
                }
                for (j = 2+lwidth; j < width; j++){
                    m_window.addch(' ');
                }

                /* Mark the current line with an arrow */
            } else if (line == mCur->exe_line) {
                switch(mCur->buf.breakpts[line]){
                    case 0:
                        if (hl_groups_get_attr (hl_groups_instance, HLG_ARROW, &attr) == -1)
                            return -1;
                        break;
                    case 1:
                        if (hl_groups_get_attr (hl_groups_instance, HLG_ENABLED_BREAKPOINT, &attr) == -1)
                            return -1;
                        break;
                    case 2:
                        if (hl_groups_get_attr (hl_groups_instance, HLG_DISABLED_BREAKPOINT, &attr) == -1)
                            return -1;
                        break;
                }
                m_window.attron(attr);
                m_window.printw(fmt, line+1);
                m_window.attroff(attr);

                Draw_current_line(line, lwidth);

                /* Look for breakpoints */
            } else if (mCur->buf.breakpts[line]){
                if (mCur->buf.breakpts[line] == 1)
                {
                    if (hl_groups_get_attr (hl_groups_instance, HLG_ENABLED_BREAKPOINT, &attr) == -1)
                        return -1;
                }
                else
                {
                    if (hl_groups_get_attr (hl_groups_instance, HLG_DISABLED_BREAKPOINT, &attr) == -1)
                        return -1;
                }
                m_window.attron(attr);
                m_window.printw(fmt, line+1);
                m_window.attroff(attr);
                if (focus)
                    m_window.attron(A_BOLD);
                m_window.addch(VERT_LINE);
                if (focus)
                    m_window.attroff(A_BOLD);
                m_window.addch(' ');

                /* I know this is rediculous, it needs to be reimplemented */
                if (sources_syntax_on__) {
                    if (line == mCur->sel_line &&
                            mCur->buf.cur_line != NULL) {
                        hl_wprintw(&m_window, mCur->buf.cur_line, 
                                width-lwidth-2, mCur->sel_col);

                    } else {
                        hl_wprintw(&m_window, mCur->buf.tlines[line], 
                                width-lwidth-2, mCur->sel_col);
                    }
                } else {
                    if (line == mCur->sel_line && 
                            mCur->buf.cur_line != NULL) {
                        hl_wprintw(&m_window, mCur->orig_buf.cur_line, 
                                width-lwidth-2, mCur->sel_col);

                    } else {
                        hl_wprintw(&m_window, 
                                mCur->orig_buf.tlines[line], 
                                width-lwidth-2, mCur->sel_col);
                    }
                }
            }
            /* Ordinary lines */
            else{
                if ( focus && mCur->sel_line == line )
                    m_window.attron(sellineno);

                m_window.printw(fmt, line+1);

                if ( focus && mCur->sel_line == line )
                    m_window.attroff(sellineno);

                if (focus)
                    m_window.attron(A_BOLD);
                m_window.addch(VERT_LINE);
                if (focus)
                    m_window.attroff(A_BOLD);
                m_window.addch(' ');

                /* I know this is rediculous, it needs to be reimplemented */
                if ( sources_syntax_on__ ) {
                    /* No special line information */
                    if (line == mCur->sel_line && 
                            mCur->buf.cur_line != NULL) {
                        hl_wprintw(&m_window, mCur->buf.cur_line, 
                                width-lwidth-2, mCur->sel_col);

                    } else {
                        hl_wprintw(&m_window, mCur->buf.tlines[line], 
                                width-lwidth-2, mCur->sel_col);
                    }
                } else {
                    /* No special line information */
                    if (line == mCur->sel_line && 
                            mCur->buf.cur_line != NULL) {
                        hl_wprintw(&m_window, mCur->orig_buf.cur_line, 
                                width-lwidth-2, mCur->sel_col);

                    } else {
                        hl_wprintw(&m_window, 
                                mCur->orig_buf.tlines[line], 
                                width-lwidth-2, mCur->sel_col);
                    }
                }
            }
        } else {
            m_window.printw("%s\n", mCur->buf.tlines[line]);
        }
    }

    m_window.move(height - (line - mCur->sel_line) , lwidth+2);
    m_window.noutrefresh();

    return 0;
}

/* source_vscroll:  Change current position in source file.
 * --------------
 * 
 *   offset:  Plus or minus number of lines to move
 */
void Sources::Vscroll(int offset)
{
    if (mCur){
        mCur->sel_line += offset;
        if (mCur->sel_line < 0)
            mCur->sel_line = 0;
        if (mCur->sel_line >= mCur->buf.length)
            mCur->sel_line = mCur->buf.length - 1;

        mCur->sel_rline = mCur->sel_line;
    }
}

/* source_hscroll:  Scroll the source file right or left in the window.
 * ---------------
 *
 *   offset:  Plus or minus number of lines to move
 */
void Sources::Hscroll(int offset)
{
    int lwidth;
    int max_width;

    if (mCur){
        lwidth    = (int) log10(mCur->buf.length)+1;
        max_width = mCur->buf.max_width - COLS + lwidth + 6;

        mCur->sel_col += offset;
        if (mCur->sel_col > max_width)
            mCur->sel_col = max_width;
        if (mCur->sel_col < 0)
            mCur->sel_col = 0;
    }
}

/* source_set_sel_line:  Set current user-selected line
 * --------------------
 *
 *   line:   Current line number
 *
 */
void Sources::Set_sel_line(int line)
{
    if (mCur){
        /* Set line (note correction for 0-based line counting) */
        mCur->sel_line = line-1;
        if (mCur->sel_line < 0)
            mCur->sel_line = 0;
        if (mCur->sel_line >= mCur->buf.length)
            mCur->sel_line = mCur->buf.length - 1;

        mCur->sel_rline = mCur->sel_line;
    }
}

/* source_set_exec_line:  Set currently executing line
 * ---------------
 *
 *   path:   Full path to the source file (may be NULL to leave unchanged)
 *   line:   Current line number (0 to leave unchanged)
 *
 * Return Value: Zero on success, non-zero on failure.
 *               5 -> file does not exist
 */
int Sources::Set_exec_line(const char *path, int line)
{
    if ( path && !Verify_file_exists(path) )
        return 5;

    /* Locate node, if path has changed */
    if (path != NULL && !(mCur = Get_node(path))){
        /* Not found -- attempt to add */
        if (Add(path))
            return 1;
        else if (!(mCur = Get_node(path)))
            return 2;
    }
    else if (path == NULL && mCur == NULL)
        return 3;

    /* Buffer the file if it's not already */
    if (!mCur->buf.tlines && Load_file(mCur))
        return 4;

    /* Update line, if set */
    if (line--){
        /* Check bounds of line */
        if (line < 0)
            line = 0;
        if (line >= mCur->buf.length)
            line = mCur->buf.length - 1;
        mCur->sel_line = mCur->exe_line = line;
    }

    return 0;
}

/* source_search_regex_init: Should be called before source_search_regex
 * -------------------------
 *   This function initializes sview before it can search for a regex
 *   It should be called every time a regex will be applied to sview before
 *   source_search_regex is called.
 *
 */
void Sources::Search_regex_init()
{
    if ( mCur == NULL)
        return;

    /* Start from beginning of line if not at same line */
    if ( mCur->sel_rline != mCur->sel_line ) {
        mCur->sel_col_rend = 0;
        mCur->sel_col_rbeg = 0;
    }

    /* Start searching at the beginning of the selected line */
    mCur->sel_rline    = mCur->sel_line;
}

/* source_search_regex: Searches for regex in current file and displays line.
 * ---------------
 *
 *   regex:  The regular expression to search for
 *           If NULL, then no regex will be tried, but the state can still
 *           be put back to its old self!
 *   opt:    If 1, Then the search is temporary ( User has not hit enter )
 *           If 2, The search is perminant
 *
 *   direction: If 0 then forward, else reverse
 *   icase:     If 0 ignore case.
 *
 * Return Value: Zero on match, 
 *               -1 if mCur is NULL
 *               -2 if regex is NULL
 *               -3 if regcomp fails
 *               non-zero on failure.
 */
int Sources::Search_regex(const char *regex, int opt,
        int direction, int icase)
{
    if ( mCur == NULL || regex == NULL ||
            strlen(regex) == 0 ) {
        mCur->buf.cur_line = NULL;
        return -1;
    }

    return hl_regex(regex, 
            (const char ** )mCur->buf.tlines, 
            (const char ** )mCur->orig_buf.tlines, 
            mCur->orig_buf.length, 
            &mCur->buf.cur_line, &mCur->sel_line,
            &mCur->sel_rline, &mCur->sel_col_rbeg, 
            &mCur->sel_col_rend, 
            opt, direction, icase);
}

/* source_free:  Release the memory associated with a source viewer.
 * ------------
 *
 */
void Sources::Free()
{
    /* Free all file buffers */
    while (mList_head != NULL)
        Del(mList_head->path);

}

/* ----------- */
/* Breakpoints */
/* ----------- */

/* source_disable_break:  Disable a given breakpoint.
 * ---------------------
 *
 *   path:   Full path to the source file
 *   line:   Line number of breakpoint
 */
void Sources::Disable_break(const char *path, int line)
{
    list_node *node;

    if ((node = Get_relative_node(path)) == NULL)
        return;

    if (node->buf.tlines == NULL)
        if (Load_file(node))
            return;

    if (line > 0 && line <= node->buf.length)
        node->buf.breakpts[line-1] = 2;
}

/* source_enable_break:  Enable a given breakpoint.
 * --------------------
 *
 *   path:   Full path to the source file
 *   line:   Line number of breakpoint
 */
void Sources::Enable_break(const char *path, int line)
{
    list_node *node;

    if ((node = Get_relative_node(path)) == NULL)
        return;

    if (node->buf.tlines == NULL)
        if (Load_file(node))
            return;

    if (line > 0 && line <= node->buf.length){
        node->buf.breakpts[line-1] = 1;
    }
}

/* source_clear_breaks:  Clear all breakpoints from all files.
 * --------------------
 *
 */
void Sources::Clear_breaks()
{
    list_node *node;

    for (node = mList_head; node != NULL; node = node->next)
        memset(node->buf.breakpts, 0, node->buf.length);
}

/**
 * Check's to see if the current source file has changed. If it has it loads
 * the new source file up.
 *
 * \param path
 * The path to the file to reload into memory
 *
 * \param force
 * Force the file to be reloaded, even if autosourcereload option is off.
 *
 * \return
 * 0 on success or -1 on error
 */
int Sources::Reload(const char *path, int force)
{
    time_t timestamp;
    list_node *cur;
    list_node *prev = NULL;
    int auto_source_reload = cgdbrc_get (CGDBRC_AUTOSOURCERELOAD)->variant.int_val;

    if ( !path )
        return -1;

    if ( Get_timestamp ( path, &timestamp ) == -1 )
        return -1;

    /* Find the target node */
    for (cur = mList_head; cur != NULL; cur = cur->next){
        if (strcmp(path, cur->path) == 0)
            break;
        prev = cur;
    }

    if (cur == NULL)
        return 1;      /* Node not found */

    if ( ( auto_source_reload || force ) && cur->last_modification < timestamp ) {

        if ( Release_file_memory ( mCur ) == -1)
            return -1;

        if ( Load_file ( cur ) )
            return -1;
    }

    return 0;
}
/* verify_file_exists: Checks to see if a file exists
 * -------------------
 *
 * Return Value: 0 if does not exist, 1 if exists 
 */
int Sources::Verify_file_exists(const char *path)
{
    struct stat st;

    /* Check for read permission of file, already exists */
    if ( stat( path, &st ) == -1 )
        return 0;

    return 1;
}

/* get_relative_node:  Returns a pointer to the node that matches the 
 * ------------------  given relative path.

 *   lpath:  Full path to source file
 *
 * Return Value:  Pointer to the matching node, or NULL if not found.
 */
Sources::list_node* Sources::Get_relative_node(const char *lpath)
{
    list_node *cur;

    for (cur = mList_head; cur != NULL; cur = cur->next)
        if ( cur->lpath ) 
            if (strcmp(lpath, cur->lpath) == 0)
                return cur;

    return NULL; 
}

/* get_node:  Returns a pointer to the node that matches the given path.
 * ---------

 *   path:  Full path to source file
 *
 * Return Value:  Pointer to the matching node, or NULL if not found.
 */
Sources::list_node* Sources::Get_node(const char *path)
{
    struct list_node *cur;

    for (cur = mList_head; cur != NULL; cur = cur->next)
        if ( cur->path )
            if (strcmp(path, cur->path) == 0)
                return cur;

    return NULL; 
}


/**
 * Get's the timestamp of a particular file.
 *
 * \param path
 * The path to the file to get the timestamp of
 *
 * \param timestamp
 * The timestamp of the file, or 0 on error.
 * 
 * \return
 * 0 on success, -1 on error.
 */
int Sources::Get_timestamp(const char *path, time_t *timestamp )
{
    struct stat s;
    int val;

    if ( !path )
        return -1;

    if ( !timestamp )
        return -1;

    *timestamp = 0;

    val = stat ( path, &s );

    if ( val ) /* Error on timestamp */
        return -1;

    *timestamp = s.st_mtime;

    return 0;
}

int Sources::Release_file_buffer(buffer *buf )
{
    int i;

    /* Nothing to free */
    if ( !buf ) 
        return 0;

    for ( i = 0; i < buf->length; ++i ) {
        free ( buf->tlines[i] );
        buf->tlines[i] = NULL;
    }

    free ( buf->tlines );
    buf->tlines = NULL;
    buf->length = 0;
    buf->cur_line = NULL;   
    buf->max_width = 0;
    free ( buf->breakpts );
    buf->breakpts = NULL;

    return 0; 
}

/** 
 * Remove's the memory related to a file.
 *
 * \param node
 * The node who's file buffer data needs to be freed.
 *
 * \return
 * 0 on success, or -1 on error.
 */
int Sources::Release_file_memory(list_node *node )
{
    if ( !node )
        return -1;

    /* Free the buffer */
    if ( Release_file_buffer ( &node->buf ) == -1 )
        return -1;

    if ( Release_file_buffer ( &node->orig_buf ) == -1 )
        return -1;

    return 0;
}
/* load_file:  Loads the file in the list_node into its memory buffer.
 * ----------
 *
 *   node:  The list node to work on
 *
 * Return Value:  Zero on success, non-zero on error.
 */
int Sources::Load_file(list_node *node)
{
    FILE *file;
    char line[MAX_LINE];
    int i;

    node->buf.length    = 0;
    node->buf.tlines    = NULL;
    node->buf.breakpts  = NULL;
    node->buf.cur_line  = NULL;
    node->buf.max_width = 0;

    /* Open file and save in original buffer.
     * I am not sure if this should be done this way in the future.
     * Maybe this data should be recieved from flex.
     */
    node->orig_buf.length = 0;
    node->orig_buf.tlines = NULL;
    node->orig_buf.breakpts  = NULL;
    node->orig_buf.cur_line  = NULL;
    node->orig_buf.max_width = 0;

    /* Stat the file to get the timestamp */
    if ( Get_timestamp ( node->path, &(node->last_modification) ) == -1 )
        return 2;

    if (!(file = fopen(node->path, "r")))
        return 1;

    while (!feof(file)){
        if (fgets(line, MAX_LINE, file)){
            int length = strlen ( line );
            if ( length > 0 ) {
                if (line[length-1] == '\n')
                    line[length-1] = 0;
                if (line[length-1] == '\r')
                    line[length-1] = 0;
            }
            if (strlen(line) > node->orig_buf.max_width)
                node->orig_buf.max_width = strlen(line);

            /* Inefficient - Reallocates memory at each line */
            node->orig_buf.length++;
            node->orig_buf.tlines = (char**)realloc(node->orig_buf.tlines, 
                    sizeof(char *) * node->orig_buf.length);
            node->orig_buf.tlines[node->orig_buf.length-1] = strdup(line);
        }
    }

    fclose(file);

    node->language = tokenizer_get_default_file_type(strrchr(node->path, '.'));

    /* Add the highlighted lines */
    if (has_colors()) {
        highlight((::list_node*)(void*)node);
    } else {
        /* Just copy the lines from the original buffer if no highlighting 
         * is possible */
        int i;
        node->buf.length = node->orig_buf.length;
        node->buf.max_width = node->orig_buf.max_width;
        node->buf.tlines = (char**)cgdb_malloc(sizeof(char *) * node->orig_buf.length);
        for ( i = 0; i < node->orig_buf.length; i++ )
            node->buf.tlines[i] = cgdb_strdup ( node->orig_buf.tlines[i] );
    }

    /* Allocate the breakpoints array */
    node->buf.breakpts = (char*) malloc(sizeof(char) * node->buf.length);
    for (i = 0; i < node->buf.length; i++)
        node->buf.breakpts[i] = 0;

    return 0;
}
/* draw_current_line:  Draws the currently executing source line on the screen
 * ------------------  including the user-selected marker (arrow, highlight,
 *                     etc) indicating this is the executing line.
 *
 *   line:   The line number
 *   lwidth: The width of the line number, used to limit printing to the width
 *           of the screen.  Kinda ugly.
 */
void Sources::Draw_current_line(int line, int lwidth)
{
    int height = 0;                     /* Height of curses window */
    int width = 0;                      /* Width of curses window */
    int i = 0, j = 0;                   /* Iterators */
    struct buffer *buf = NULL;          /* Pointer to the source buffer */
    char *text = NULL;                  /* The current line (highlighted) */
    char *otext = NULL;                 /* The current line (unhighlighted) */
    unsigned int length = 0;            /* Length of the line */
    int column_offset = 0;              /* Text to skip due to arrow */
    int arrow_attr;
    int highlight_attr;
    enum ArrowStyle config_arrowstyle = cgdbrc_get(CGDBRC_ARROWSTYLE)->variant.arrow_style;
    int highlight_tabstop = cgdbrc_get(CGDBRC_TABSTOP)->variant.int_val;

    if (hl_groups_get_attr(hl_groups_instance, HLG_ARROW, &arrow_attr) == -1)
        return;

    if (hl_groups_get_attr(hl_groups_instance, HLG_LINE_HIGHLIGHT, &highlight_attr) == -1)
        return;

    /* Initialize height and width */
    m_window.getmaxyx(height, width);

    /* If syntax highlighting is on, point to the colored buffer. */
    if (sources_syntax_on__) {
        buf = &(mCur->buf);
    } else {
        buf = &(mCur->orig_buf);
    }

    /* If the current selected line is the line executing, use cur_line */
    if (line == mCur->sel_line && buf->cur_line != NULL) {
        text = buf->cur_line;
    } else {
        text = buf->tlines[line];
    }
    otext = mCur->orig_buf.tlines[line];
    length = strlen(otext);

    /* Draw the appropriate arrow, if applicable */
    switch (config_arrowstyle) {

        case ARROWSTYLE_SHORT:

            m_window.attron(arrow_attr);
            m_window.addch(ACS_LTEE);
            m_window.addch('>');
            m_window.attroff(arrow_attr);
            break;

        case ARROWSTYLE_LONG:

            m_window.attron(arrow_attr);
            m_window.addch(ACS_LTEE);

            /* Compute the length of the arrow, respecting tab stops, etc.*/
            for (i = 0; i < length-1 && isspace(otext[i]); i++) {

                /* Oh so cryptic*/
                int offset = otext[i] != '\t' ? 1 :
                    highlight_tabstop - (column_offset % highlight_tabstop);

                if (!isspace(otext[i+1])) {
                    offset--;
                }

                column_offset += offset;
            }
            column_offset -= mCur->sel_col;
            if (column_offset < 0) {
                column_offset = 0;
            }

            /* Now actually draw the arrow*/
            for (j = 0; j < column_offset; j++) {
                m_window.addch(ACS_HLINE);
            }

            m_window.addch('>');
            m_window.attroff(arrow_attr);
            break;

        case ARROWSTYLE_HIGHLIGHT:
            m_window.addch(VERT_LINE);
            m_window.addch(' ');

            m_window.attron(highlight_attr);
            for (i = 0; i < width-lwidth-2; i++) {
                if (i < length) {
                    m_window.addch(otext[i]);
                } else {
                    m_window.addch(' ');
                }
            }
            m_window.attroff(highlight_attr);

            return;
    }

    /* Finally, print the source line */
    hl_wprintw(&m_window, text, width-lwidth-2, 
            mCur->sel_col + column_offset);
}

